const WebSocket = require('ws');
const util = require('./lib/util');
const jwt = require('./model/jwt');
const crypto = require('crypto-js');
const redis = require('./boot/redis').redisClient;
const sub = require('./boot/redis').redisSubscriber;
const db = require('./model/db');
let wss = new WebSocket.Server({
	port: 3000
});

// 进程编号
const processCode = 'process-' + crypto.MD5( util.uuid() ).toString();
console.log(`进程编号: ${processCode}`);

/**
 * 广播消息到本进程连接
 * @param {string} msg
 */
wss.broadcast = function (msg) {
	wss.clients.forEach((client) => {
		client.send(msg);
	});
};

let sessions = {};
wss.on('connection', async (socket, req) => {
	let session = await getSession(req.url);
	sessions[session.user.id]['socket'] = socket;

	socket.on('message', (message) => {
		try {
			let router = JSON.parse(message);
			let controller = `${router[0]}_controller`;
			let action = util.underline2camel(`on_${router[1]}`);
			require(`./controller/${controller}`)(socket, processCode, session)[action](router[2] || '{}');
		}
		catch (err) {
			console.log(err);
		}
	});

	socket.on('close', async () => {
		delete sessions[session.user.id]['socket'];
		let redisSession = JSON.parse(await redis.hget('session', session.user.id));
		redisSession.status = 'offline';
		redis.hset('session', session.user.id, JSON.stringify(redisSession));

		// 朕已阅
		let start = Date.now() - 7*3600000;
		let sql = "UPDATE diss_chat_log SET isread = 'yes' WHERE receiver = :uid AND create_time > :time";
		db.query(sql, {
			uid: session.user.id,
			time: start
		});
	});

	// 错误处理
	socket.on('error', (err) => {
        console.log(err);
    });
});

/**
 * @param {string} url 
 */
async function getSession(url) {
	let accessToken = url.replace('/?accessToken=', '');
	const errorMessage = {isGuest: true};
	if (util.isEmpty(url) || util.isEmpty(accessToken)) {
		return errorMessage;
	}

	try {
		var playload = await jwt.decode(accessToken);
	}
	catch (err) {
		console.log(err);
		return errorMessage;
	}

	playload.isGuest = false;
	sessions[playload.user.id] = playload;

	let redisSession = JSON.parse(await redis.hget('session', playload.user.id));
	redisSession.processCode = processCode;
	redisSession.status = 'online';
	await redis.hset('session', playload.user.id, JSON.stringify(redisSession));
	return playload;
}

sub.subscribe(processCode);
sub.on('message', (channel, message) => {
	let arr = JSON.parse(message);
	let event = arr[0];
	let data = arr[1];
	switch (event) {
		case 'receiveMessage': {
			let sendTo = data['sendTo'];
			let socket = sessions[sendTo]['socket'];
			socket.send(message);

			// 聊天日志缓存
			let doc = {
				sender: data.sendFrom.id,
				receiver: sendTo,
				content: data.content,
				create_time: Date.now()
			};
			redis.rpush('chatLogCache', JSON.stringify(doc));
			break;	
		}
		default:
			break;
	}
});

// 消息持久化
setInterval(async () => {
	let docs = [];
	let last = Date.now();
	while(true) {
		let now = Date.now();
		if (now-last > 500) break;
		let item = await redis.lpop('chatLogCache');
		if (!util.isEmpty(item)) docs.push(JSON.parse(item));
		else break;
	}
	if (docs.length > 0) db.insertAll('diss_chat_log', docs);
}, 3000);